﻿using System;
using System.IO;
using System.Json;
using System.Text;
using System.Threading;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.IO.Compression;

#if VALKYRIE_CRUSADE
using zlib;
#endif

namespace Tsanie.Common
{
    class NetUtility
    {
        const int BUFFER_LENGTH = 1024;

#if VALKYRIE_CRUSADE
        const string USER_AGENT = "sg2-agent/2.1.1 (android/4.4.2/aries/xx/xx)";
#endif
        const string USER_AGENT_CHROME = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1564.0 Safari/537.36";
        const string DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded";

        public int DefaultDelay = 0;

        static bool remoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        //class MyVerify : Java.Lang.Object, IHostnameVerifier
        //{
        //    public bool Verify(string hostname, ISSLSession session)
        //    {
        //        return true;
        //    }
        //}

        static string GetException(Exception ex)
        {
            string result = ex.StackTrace;
            int index = result.IndexOf("--- End of managed exception stack trace ---\n");
            if (index > 0)
            {
                result = result.Substring(index + 45);
                index = result.IndexOf("\n\t");
                if (index > 0)
                {
                    result = result.Substring(0, index);
                }
            }
            else
            {
                result = ex.GetType().FullName + ": " + ex.Message;
            }

            result = "{\"code\":-1,\"status\":\"-1\",\"msg\":\"" + result.Replace("\"", "\\\"") + "\"";
            if (ex.InnerException != null)
            {
                result += ",\"inner_ex\":" + GetException(ex.InnerException);
            }
            result += "}";
            return result;
        }

#if VALKYRIE_CRUSADE
        static byte[] getAuthKey()
        {
            byte[] arrayOfByte1 = { 14, 34, 115, 120, 28, 120, 25, 26, 127, 120, 59, 10, 126, 61, 123, 44, 122, 127, 2, 115, 120, 120, 13, 1, 50, 28, 5, 33, 125, 115 };
            byte[] arrayOfByte2 = new byte[31];
            for (int i = 0; i < 30; i++)
            {
                arrayOfByte2[i] = (byte)(0x4B ^ arrayOfByte1[i]);
            }
            return arrayOfByte2;
        }

        public static string GetSignatureKey(string data)
        {
            HMACSHA256 hmac = new HMACSHA256(getAuthKey());
            byte[] signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
            return "Nubee method=HMAC-SHA256,version=1.0,signature=" + signature.GetHexString();
        }

        public static string GetHardwareHash(string hardware)
        {
            byte[] iv = new byte[] { 4, 8, 12, 13, 9, 0, 2, 13, 6, 13, 13, 9, 14, 11, 1, 15 };
            byte[] key = new byte[] { 5, 5, 9, 11, 0, 10, 8, 5, 0, 7, 12, 4, 6, 0, 12, 14, 15, 8, 15, 14, 8, 9, 15, 11, 8, 1, 4, 7, 6, 15, 12, 13 };

            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            aes.IV = iv;
            aes.Key = key;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            ICryptoTransform trans = aes.CreateEncryptor();
            byte[] data = Encoding.UTF8.GetBytes(hardware);
            data = trans.TransformFinalBlock(data, 0, data.Length);
            aes.Clear();

            return data.GetHexString();
        }
#endif

        public string URL { get; set; }
        public Action<long> Step { get; set; }

        #region - get -

        public byte[] GetData()
        {
            byte[] s;
            using (MemoryStream ms = new MemoryStream())
            {
                GetData(ms, 0);
#if VALKYRIE_CRUSADE
                s = DecompressData(ms.ToArray());
#else
                s = ms.ToArray();
#endif
            }

            if (DefaultDelay > 0)
            {
                Thread.Sleep(DefaultDelay);
            }
            return s;
        }

        void GetData(Stream ws, int cnt)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.URL);
            request.Timeout = 5000;
            request.Accept = "application/json;q=0.9,*/*;q=0.8";
            request.Headers["Accept-Encoding"] = "gzip,deflate";
            request.Headers["Accept-Language"] = "zh-CN,zh;q=0.8,zh-TW;q=0.6,en;q=0.4,en-US;q=0.2,ja;q=0.2";
            request.Headers["Cache-Control"] = "max-age=0"; // no-cache
            //request.Headers["Pragma"] = "no-cache";
            request.KeepAlive = true;
            request.UserAgent = USER_AGENT_CHROME;
            // ssl
            ServicePointManager.ServerCertificateValidationCallback = remoteCertificateValidationCallback;

            try
            {
                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    using (var stream = response.GetResponseStream())
                    {
                        switch (response.ContentEncoding)
                        {
                            case "gzip":
                                writeStream(new GZipStream(stream, CompressionMode.Decompress), ws);
                                break;

                            case "deflate":
                                writeStream(new DeflateStream(stream, CompressionMode.Decompress), ws);
                                break;

                            default:
                                writeStream(stream, ws);
                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (cnt < 3)
                {
                    Thread.Sleep(600);
                    GetData(ws, cnt + 1);
                }
                else
                {
                    byte[] s = Encoding.UTF8.GetBytes(GetException(ex));
                    ws.Write(s, 0, s.Length);
                }
            }
        }

        void writeStream(Stream stream, Stream ws)
        {
            byte[] buffer = new byte[BUFFER_LENGTH];
            int count;
            while ((count = stream.Read(buffer, 0, BUFFER_LENGTH)) > 0)
            {
                ws.Write(buffer, 0, count);
                if (this.Step != null)
                {
                    this.Step(ws.Position);
                }
            }
            ws.Flush();
        }

        #endregion

        #region - post -

//        public static byte[] PostData(string url, string data)
//        {
//            return PostData(url, null, data);
//        }

//        public static byte[] PostData(string url, Action<HttpURLConnection> handler, string data)
//        {
//            URL u = new URL(url);
//            HttpURLConnection conn = (HttpURLConnection)u.OpenConnection();
//            conn.DoOutput = true;
//            conn.DoInput = true;
//            conn.RequestMethod = "POST";
//            conn.UseCaches = false;
//            conn.InstanceFollowRedirects = true;
//#if VALKYRIE_CRUSADE
//            conn.SetRequestProperty("User-Agent", USER_AGENT);
//#else
//            conn.SetRequestProperty("User-Agent", USER_AGENT_CHROME);
//#endif
//            conn.SetRequestProperty("Accept", "*/*");
//            conn.SetRequestProperty("Content-Type", DEFAULT_CONTENT_TYPE);
//            if (handler != null)
//            {
//                handler(conn);
//            }
//            conn.ConnectTimeout = 10000;
//            conn.Connect();

//            byte[] d = Encoding.ASCII.GetBytes(data);
//            // ssl
//            //ServicePointManager.ServerCertificateValidationCallback = remoteCertificateValidationCallback;

//            byte[] s;
//            try
//            {
//                using (Stream requestStream = conn.OutputStream)
//                {
//                    requestStream.Write(d, 0, d.Length);
//                }

//                byte[] result;
//                using (Stream responseStream = conn.InputStream)
//                using (MemoryStream ms = new MemoryStream())
//                {
//                    byte[] buffer = new byte[BUFFER_LENGTH];
//                    int count;
//                    while ((count = responseStream.Read(buffer, 0, BUFFER_LENGTH)) > 0)
//                    {
//                        ms.Write(buffer, 0, count);
//                    }
//                    ms.Flush();
//                    result = ms.ToArray();
//                }

//#if VALKYRIE_CRUSADE
//                s = DecompressData(result);
//#else
//                s = result;
//#endif
//            }
//            catch (Exception ex)
//            {
//                s = Encoding.UTF8.GetBytes(GetException(ex));
//            }

//            if (DefaultDelay > 0)
//            {
//                Thread.Sleep(DefaultDelay);
//            }
//            return s;
//        }

//        public static JsonValue Post(string url, string data)
//        {
//            return Post(url, null, data);
//        }

//        public static JsonValue Post(string url, string data, int retry)
//        {
//            var result = Post(url, null, data);
//            if (result != null && result["code"] < 0)
//            {
//                // timeout, etc.
//                if (retry > 0)
//                {
//                    // retry after 5s.
//                    Thread.Sleep(5000);
//                    return Post(url, data, retry - 1);
//                }
//            }
//            return result;
//        }

//        public static JsonValue Post(string url, Action<HttpURLConnection> handler, string data)
//        {
//#if VALKYRIE_CRUSADE
//            string s = Decompress(PostData(url, handler, data));
//#else
//            string s = Encoding.UTF8.GetString(PostData(url, handler, data));
//#endif
//            try
//            {
//                return JsonValue.Parse(s);
//            }
//            catch (Exception ex)
//            {
//                return JsonValue.Parse(GetException(ex));
//            }
//        }

//#if VALKYRIE_CRUSADE
//        static byte[] DecompressData(byte[] data)
//        {
//            if (data.Length > 6 && data[0] == 0x43 && data[1] == 0x4f && data[2] == 0x4d && data[3] == 0x50)
//            {
//                // "COMPx;"
//                int i = 4;
//                for (; i < data.Length; i++)
//                {
//                    if (data[i] == 0x3b)
//                    {
//                        // ";"
//                        i++;
//                        break;
//                    }
//                }
//                byte[] result;
//                using (ZInputStream input = new ZInputStream(new MemoryStream(data, i, data.Length - i)))
//                {
//                    using (MemoryStream ms = new MemoryStream())
//                    {
//                        byte[] buffer = new byte[BUFFER_LENGTH];
//                        int count;
//                        while ((count = input.read(buffer, 0, BUFFER_LENGTH)) > 0)
//                        {
//                            ms.Write(buffer, 0, count);
//                        }
//                        ms.Flush();
//                        result = ms.ToArray();
//                    }
//                }

//                return result;
//            }
//            return data;
//        }

//        static string Decompress(byte[] data)
//        {
//            int len = data.Length;
//            if (data[len - 1] == 0)
//            {
//                len--;
//            }
//            return Encoding.UTF8.GetString(data, 0, len);  // Convert.ToBase64String (data);
//        }
//#endif

        #endregion
    }
}
